/*
 *  (c) 2015 fullhan.com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */

#include <common.h>
#include <environment.h>
#include <asm/arch/hardware.h>
#if defined(CONFIG_RESET_PHY_R) && defined(CONFIG_MACB)
#include <net.h>
#endif
#include <netdev.h>
#include <asm-generic/gpio.h>
#include "pinctrl.h"
#include "board_config.h"
#ifdef CONFIG_FH_DMA
#include <fh_dma.h>
#endif
#include <phy.h>
#include <spi.h>
DECLARE_GLOBAL_DATA_PTR;
extern env_t *env_ptr;

#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x)		(sizeof(x) / sizeof((x)[0]))
#endif
struct phy_reg_cfg phy_config_array[] = {
	{
		.id = FH_GMAC_PHY_RTL8201,
		.list = {
			{W_PHY, 0x1f, 0x7},
			{W_PHY, 0x10, 0x1ffc},
			{W_PHY, 0x1f, 0},
			{0},
		},
		.inf_sup = PHY_INTERFACE_MODE_RMII,
	},
	{
		.id = FH_GMAC_PHY_INTERNAL_V2,
		.list = {
			{0},
		},
		.inf_sup = PHY_INTERFACE_MODE_MII,
	},
	{0},
};

#ifdef CONFIG_FH_GMAC
void fh_gmac_phy_reset(void)
{
	//SET_REG(REG_PMU_PAD_MAC_RXDV_CFG, 0x01101030);
	//gpio_direction_output(CONFIG_GPIO_EMACPHY_RXDV, 0);
	gpio_direction_output(CONFIG_GPIO_EMACPHY_RESET, 1);
	udelay(150000);
	gpio_direction_output(CONFIG_GPIO_EMACPHY_RESET, 0);
	udelay(10000);
	gpio_direction_output(CONFIG_GPIO_EMACPHY_RESET, 1);
	//SET_REG(REG_PMU_PAD_MAC_RXDV_CFG, 0x00101030);
	udelay(100000);
}

int fh_get_gmac_phy_reset_pin_no(){
	return CONFIG_GPIO_EMACPHY_RESET;
}
#endif

#ifdef CONFIG_USB
void fh_usb_power_enable(void)
{
	SET_REG_M(REG_PMU_USB_SYS1, 0x0, 0x400);
	gpio_direction_output(CONFIG_GPIO_USB_PWREN, 1);
	
	SET_REG_M(REG_PMU_USB_SYS0, 0x11, 0x11);
	udelay(1000);
	SET_REG_M(REG_PMU_USB_SYS0, 0x0, 0x11);
}
#endif

/* Select SD1 function from SPI0 to SD1 */
void fh_sd1_function_switch(void)
{
	SET_REG_M(REG_PMU_SD1_FUNC_SEL, 0x3, 0x3);
}

/* Get boot mode */
int get_boot_media(void)
{
	int boot_mode;

	boot_mode = GET_REG(REG_PMU_BOOT_MODE);

	return boot_mode;
}

/* Get which eMMC port to save env */
int mmc_get_env_dev(void)
{
	int boot_mode = 0;
	int dev = 0;

	boot_mode = GET_REG(REG_PMU_BOOT_MODE);
	switch(boot_mode)
	{
		case FH_SD0_EMMC_FLASH:
		default:
			dev = 0;
			break;
		case FH_SD1_EMMC_FLASH:
			dev = 1;
			break;
	}

	return dev;
}

void clock_init(void)
{
	SET_REG(REG_PMU_SCU_PLL_WREN, 0x706c6c63);

	//SPI0
	SET_REG_M(REG_PMU_CLK_DIV3, 0xb, 0xff);
	//GMAC
	SET_REG_M(REG_PMU_CLK_DIV6, 0x1000000, 0xf000000);
	//UART0
	SET_REG_M(REG_PMU_CLK_DIV4, 0x5, 0x1f);
	//TIMER0
	SET_REG_M(REG_PMU_CLK_DIV5, 0x630000, 0xff0000);
	//PTS
	SET_REG_M(REG_PMU_CLK_DIV2, 0x63, 0x1ff);
	//WDT
	SET_REG_M(REG_PMU_CLK_DIV5, 0xc700, 0xff00);

#ifdef CONFIG_FH_MMC
	SET_REG_M(REG_PMU_CLK_GATE, 0x0, 0x400);
	SET_REG_M(REG_PMU_CLK_GATE1, 0x0, 0x8);
	SET_REG_M(REG_PMU_CLK_GATE1, 0x8100, 0xff00);
	/* Set Clock */
	SET_REG_M(REG_PMU_CLK_DIV3, 0x2000000, 0x0f000000);
	SET_REG_M(REG_PMU_CLK_GATE, 0x0, 0x200);
	SET_REG_M(REG_PMU_CLK_GATE1, 0x0, 0x4);
	SET_REG_M(REG_PMU_CLK_GATE1, 0x810000, 0xff0000);
#endif


#ifdef CONFIG_FH_GMAC
	SET_REG_M(REG_PMU_CLK_GATE, 0x0, 0x12000000);
#endif

#ifdef CONFIG_FH_SERIAL
	SET_REG_M(REG_PMU_CLK_GATE, 0x0, 0x2000);
#endif
	SET_REG(REG_PMU_SCU_PLL_WREN, 0);

}

int check_wdt_status(void)
{
	return 0;
}

/*extern void hw_board_wdt_init(void);*/

int board_init(void)
{
	/* Enable Ctrlc */
	/*console_init_f();*/
	gd->bd->bi_arch_number = MACH_TYPE_FH8858V300;
	/* adress of boot parameters */
	gd->bd->bi_boot_params = PHYS_SDRAM + 0x100;
	fh_pinctrl_init(PMU_REG_BASE + 0x80);
	clock_init();
	/*hw_board_wdt_init();*/
#ifdef CONFIG_USB
	fh_usb_power_enable();
#endif
#if defined CONFIG_FH_DMA || defined CONFIG_FH_AXI_DMA
	dma_init();
#endif
#if defined CONFIG_FH_AES && defined CONFIG_FH_ALGORITHM_CORE
        fh_aes_probe();
#endif
#ifdef CONFIG_FH_SPI
extern struct spi_slave *fh_spi_setup_slave(unsigned int bus, unsigned int cs,
				unsigned int max_hz, unsigned int mode);
	spi_init();
	spi_bus_register(0, fh_spi_setup_slave);
	spi_bus_register(1, fh_spi_setup_slave);
	spi_bus_register(2, fh_spi_setup_slave);
#endif
	return 0;
}

int dram_init(void)
{
	gd->ram_size = PHYS_SDRAM_SIZE;
	return 0;
}

void dram_init_banksize(void)
{
	gd->bd->bi_dram[0].start = PHYS_SDRAM;
	gd->bd->bi_dram[0].size = PHYS_SDRAM_SIZE;
}

#ifdef CONFIG_RESET_PHY_R
void reset_phy(void)
{
#ifdef CONFIG_MACB
	/*
	 * Initialize ethernet HW addr prior to starting Linux,
	 * needed for nfsroot
	 */
	eth_init(gd->bd);
#endif
}
#endif


struct gmac_plat_info gmac_plat_data = {
	.regs = GMAC_REG_BASE,
	.id = 0,
	.phy_driver_list[0] = "Generic PHY",
	.phy_driver_list[1] = 0,
	.p_cfg_array = phy_config_array,
};

void *get_platform_eth_info(u32 id){
	return &gmac_plat_data;
}

int phy_inf_set(struct phy_device *phydev, int inf){

	SET_REG(REG_PMU_SCU_PLL_WREN,0x706c6c63);

	if(inf == PHY_INTERFACE_MODE_RMII){
		/*bit 100 : rmii; bit 000 : mii*/
		SET_REG_M(REG_PMU_EFHY_REG0, 4, 7);
	}
	else{
		SET_REG_M(REG_PMU_EFHY_REG0, 0, 7);
	}

	SET_REG(REG_PMU_SCU_PLL_WREN,0);
}

static int phy_sel_func(struct phy_device *phydev, int phy_sel)
{
	SET_REG(REG_PMU_SCU_PLL_WREN,0x706c6c63);

	if(phy_sel == EXTERNAL_PHY){
		//close inside phy
		SET_REG_M(REG_PMU_EFHY_REG0, 0 << INSIDE_PHY_ENABLE_BIT_POS, 1 << INSIDE_PHY_ENABLE_BIT_POS);
		SET_REG_M(REG_PMU_CLK_GATE1, 1 << INSIDE_CLK_GATE_BIT_POS, 1 << INSIDE_CLK_GATE_BIT_POS);
		//set div
		SET_REG_M(REG_PMU_CLK_DIV6, 1 << MAC_REF_CLK_DIV_BIT_POS,
		MAC_REF_CLK_DIV_MASK << MAC_REF_CLK_DIV_BIT_POS);
		// //set pad

		//eth ref clk out Ungate
		SET_REG_M(REG_PMU_CLK_GATE, 0 << ETH_REF_CLK_OUT_GATE_BIT_POS,
		1 << ETH_REF_CLK_OUT_GATE_BIT_POS);
		//eth rmii clk Ungate
		SET_REG_M(REG_PMU_CLK_GATE, 0 << ETH_RMII_CLK_OUT_GATE_BIT_POS,
		1 << ETH_RMII_CLK_OUT_GATE_BIT_POS);
		//switch mac clk in
		SET_REG_M(REG_PMU_CLK_SEL, 1 << IN_OR_OUT_PHY_SEL_BIT_POS,
		1 << IN_OR_OUT_PHY_SEL_BIT_POS);
	}
	else{
		//inside phy enable
		SET_REG_M(REG_PMU_EFHY_REG0, 1 << INSIDE_PHY_ENABLE_BIT_POS, 1 << INSIDE_PHY_ENABLE_BIT_POS);
		//set pad no need
		//eth ref clk out gate
		SET_REG_M(REG_PMU_CLK_GATE, 1 << ETH_REF_CLK_OUT_GATE_BIT_POS,
		1 << ETH_REF_CLK_OUT_GATE_BIT_POS);
		//eth rmii clk gate
		SET_REG_M(REG_PMU_CLK_GATE, 1 << ETH_RMII_CLK_OUT_GATE_BIT_POS,
		1 << ETH_RMII_CLK_OUT_GATE_BIT_POS);
		//inside phy clk Ungate
		SET_REG_M(REG_PMU_CLK_GATE1, 0 << INSIDE_CLK_GATE_BIT_POS, 1 << INSIDE_CLK_GATE_BIT_POS);
		//int rmii refclk mux
		SET_REG_M(REG_PMU_CLK_SEL, 0 << IN_OR_OUT_PHY_SEL_BIT_POS,
		1 << IN_OR_OUT_PHY_SEL_BIT_POS);

	}
	SET_REG(REG_PMU_SCU_PLL_WREN,0);
}

static int gmac_set_spd(int speed){
	int reg;

	SET_REG(REG_PMU_SCU_PLL_WREN,0x706c6c63);

	reg = GET_REG(PMU_REG_BASE + GMAC_SPEED_SWITCH_PMU_REG_OFFSET);
	reg &= ~(1 << GMAC_SPEED_SWITCH_BIT_POS);
	if (speed == GMAC_SPEED_10M)
		reg |= (!GMAC_SPEED_SWITCH_100M_VAL) << GMAC_SPEED_SWITCH_BIT_POS;
	else
		reg |= GMAC_SPEED_SWITCH_100M_VAL << GMAC_SPEED_SWITCH_BIT_POS;
	SET_REG(PMU_REG_BASE + GMAC_SPEED_SWITCH_PMU_REG_OFFSET, reg);

	SET_REG(REG_PMU_SCU_PLL_WREN,0);

}




#define BITS_PER_LONG 32
#define BITMAP_LAST_WORD_MASK(nbits) (~0UL >> (-(nbits) & (BITS_PER_LONG - 1)))

static inline unsigned long find_first_bit(const unsigned long *addr, unsigned long size)
{
	unsigned long idx;

	for (idx = 0; idx * BITS_PER_LONG < size; idx++) {
		if (addr[idx])
			return min(idx * BITS_PER_LONG + __ffs(addr[idx]), size);
	}

	return size;
}


unsigned long find_last_bit(const unsigned long *addr, unsigned long size)
{
	if (size) {
		unsigned long val = BITMAP_LAST_WORD_MASK(size);
		unsigned long idx = (size-1) / BITS_PER_LONG;

		do {
			val &= addr[idx];
			if (val)
				return idx * BITS_PER_LONG + __fls(val);

			val = ~0ul;
		} while (idx--);
	}
	return size;
}

enum {
	CP_VAL_0,
	CP_VAL_1,
	VAL_100M_0,
	VAL_100M_1,
	VAL_10M_0,
	VAL_10M_1,
	TRAIN_MAX_SIZE,
};

static unsigned int train_100_array[] = {
	31, 30, 29, 28,
	27, 26, 25, 24,
	23, 22, 21, 20,
	19, 18, 17, 16,
	0,  1,  2,  3,
	4,  5,  6,  7,
	8,  9,  10, 11,
	12, 13, 14, 15,
};

static unsigned int train_10_array[] = {
	31, 30, 29, 28,
	27, 26, 25, 24,
	23, 22, 21, 20,
	19, 18, 17, 16,
	0,  1,  2,  3,
	4,  5,  6,  7,
	8,  9,  10, 11,
	12, 13, 14, 15,
};

static unsigned int train_cp_array[] = {
	0, 1, 2, 3,
};
#define REFIX_CP_TRAIN_DATA_OFFSET	    (0)
#define REFIX_100M_TRAIN_DATA_OFFSET    (0)
#define REFIX_10M_TRAIN_DATA_OFFSET	    (0)

struct s_train_val train_array[TRAIN_MAX_SIZE] = {

		[CP_VAL_0].name = "cp_val_0",
		[CP_VAL_0].src_add = REG_PMU_EPHY_PARAM,
		[CP_VAL_0].src_mask = 0xc0,
		[CP_VAL_0].src_vaild_index = 15,
		[CP_VAL_0].dst_add = 0x18,
		[CP_VAL_0].dst_vaild_index = 22,
		[CP_VAL_0].bind_train_array = train_cp_array,
		[CP_VAL_0].bind_train_size = ARRAY_SIZE(train_cp_array),
		[CP_VAL_0].usr_train_offset =
		REFIX_CP_TRAIN_DATA_OFFSET,

		[CP_VAL_1].name = "cp_val_1",
		[CP_VAL_1].src_add = REG_PMU_EPHY_PARAM,
		[CP_VAL_1].src_mask = 0xc0,
		[CP_VAL_1].src_vaild_index = 15,
		[CP_VAL_1].dst_add = 0xb7c,
		[CP_VAL_1].dst_vaild_index = 6,
		[CP_VAL_1].bind_train_array = train_cp_array,
		[CP_VAL_1].bind_train_size = ARRAY_SIZE(train_cp_array),
		[CP_VAL_1].usr_train_offset =
		REFIX_CP_TRAIN_DATA_OFFSET,

		[VAL_100M_0].name = "val_100M_0",
		[VAL_100M_0].src_add = REG_PMU_EPHY_PARAM,
		[VAL_100M_0].src_mask = 0x1f,
		[VAL_100M_0].src_vaild_index = 5,
		[VAL_100M_0].dst_add = 0x24,
		[VAL_100M_0].dst_vaild_index = 8,
		[VAL_100M_0].bind_train_array = train_100_array,
		[VAL_100M_0].bind_train_size = ARRAY_SIZE(train_100_array),
		[VAL_100M_0].usr_train_offset =
		REFIX_100M_TRAIN_DATA_OFFSET,

		[VAL_100M_1].name = "val_100M_1",
		[VAL_100M_1].src_add = REG_PMU_EPHY_PARAM,
		[VAL_100M_1].src_mask = 0x1f,
		[VAL_100M_1].src_vaild_index = 5,
		[VAL_100M_1].dst_add = 0x1308,
		[VAL_100M_1].dst_vaild_index = 8,
		[VAL_100M_1].bind_train_array = train_100_array,
		[VAL_100M_1].bind_train_size = ARRAY_SIZE(train_100_array),
		[VAL_100M_1].usr_train_offset =
		REFIX_100M_TRAIN_DATA_OFFSET,

		[VAL_10M_0].name = "val_10M_0",
		[VAL_10M_0].src_add = REG_PMU_EPHY_PARAM,
		[VAL_10M_0].src_mask = 0x1f00,
		[VAL_10M_0].src_vaild_index = 13,
		[VAL_10M_0].dst_add = 0x24,
		[VAL_10M_0].dst_vaild_index = 16,
		[VAL_10M_0].bind_train_array = train_10_array,
		[VAL_10M_0].bind_train_size = ARRAY_SIZE(train_10_array),
		[VAL_10M_0].usr_train_offset =
		REFIX_10M_TRAIN_DATA_OFFSET,

		[VAL_10M_1].name = "val_10M_1",
		[VAL_10M_1].src_add = REG_PMU_EPHY_PARAM,
		[VAL_10M_1].src_mask = 0x1f00,
		[VAL_10M_1].src_vaild_index = 13,
		[VAL_10M_1].dst_add = 0x1308,
		[VAL_10M_1].dst_vaild_index = 0,
		[VAL_10M_1].bind_train_array = train_10_array,
		[VAL_10M_1].bind_train_size = ARRAY_SIZE(train_10_array),
		[VAL_10M_1].usr_train_offset =
		REFIX_10M_TRAIN_DATA_OFFSET,
};

void dump_phy_train_val(struct s_train_val *p_train)
{
	printf("------------GAP_LINE---------------\n");
	printf("name            : %s\n", p_train->name);
	printf("src_add         : %08x\n", p_train->src_add);
	printf("src_mask        : %08x\n", p_train->src_mask);
	printf("src_vaild_index : %08x\n", p_train->src_vaild_index);
	printf("dst_add         : %08x\n", p_train->dst_add);
	printf("dst_vaild_index : %08x\n", p_train->dst_vaild_index);
	printf("usr_train_offset: %d\n", p_train->usr_train_offset);
}


void parse_train_data(struct s_train_val *p_train)
{
	unsigned long test_data;
	int i;
	unsigned int temp_data;
	unsigned int vaild_bit_pos;
	int max_offset, low_offset;
	unsigned int *array;
	unsigned int size;

	array = p_train->bind_train_array;
	size = p_train->bind_train_size;
	test_data = p_train->src_mask;
	vaild_bit_pos = find_first_bit(&test_data, 32);

	if ((!array) ||
	((p_train->src_mask >> vaild_bit_pos) != (size - 1))) {
		printf("para error..\n");
		return;
	}

	temp_data = GET_REG(p_train->src_add);
	if (temp_data & (1 << p_train->src_vaild_index)) {
		/*got vaild val*/
		temp_data &= p_train->src_mask;
		temp_data >>= vaild_bit_pos;
		printf("[%s] :: train val %d\n", p_train->name, temp_data);
		/*find index*/
		for (i = 0; i < size; i++) {
			if (temp_data == array[i]) {
				max_offset = (size - 1) - i;
				low_offset = 0 - i;
				break;
			}
		}

		if ((p_train->usr_train_offset < low_offset) ||
		(p_train->usr_train_offset > max_offset)) {
			printf("offset [%d] should limit in [%d , %d]\n",
			p_train->usr_train_offset, low_offset, max_offset);
			return;
		}
		//fix array idx..
		i += p_train->usr_train_offset;
		temp_data = array[i];
		/*printf("fix idx :: [%d] = %d\n", i, temp_data);*/
	} else {
		temp_data = 0;
		printf("[%s] :: use default train data %d\n",
		p_train->name, temp_data);
	}
	temp_data &= (p_train->src_mask >> vaild_bit_pos);
	SET_REG_M(REG_EPHY_BASE + p_train->dst_add,
	temp_data << p_train->dst_vaild_index,
	(p_train->src_mask >> vaild_bit_pos) << p_train->dst_vaild_index);
}


static void fh_internal_phy_reset(void){
	int idx;

	printf("fh internal phy reset go...\n ");
	SET_REG(REG_PMU_SCU_PLL_WREN,0x706c6c63);

	SET_REG_M(REG_EPHY_BASE + 0, 0, 0x3<<7);
	SET_REG(REG_EPHY_BASE + 0x28, 0x00247875);
	SET_REG(REG_EPHY_BASE + 0x1274, 0x3c3);

	SET_REG_M(REG_EPHY_BASE + 0x1370, 0x0, 1<<3);
	SET_REG(REG_EPHY_BASE + 0x1274, 0x3c3);
	SET_REG(REG_EPHY_BASE + 0x0b6C, 0xf04);
	SET_REG(REG_EPHY_BASE + 0x1340, 0x0000);
	SET_REG(REG_EPHY_BASE + 0x0bA8, 0x20);
	SET_REG(REG_EPHY_BASE + 0x0018, 0x00c02800);
	SET_REG(REG_EPHY_BASE + 0x001c, 0x00810000);
	SET_REG(REG_EPHY_BASE + 0x0020, 0x0000340c);
	SET_REG(REG_EPHY_BASE + 0x0024, 0x10131500);
	SET_REG(REG_EPHY_BASE + 0x0028, 0x01402800);
	SET_REG(REG_EPHY_BASE + 0x001c, 0xa1800000);
	SET_REG(REG_EPHY_BASE + 0x0028, 0x00402800);
	SET_REG(REG_EPHY_BASE + 0x0014, 0x06000000);
	SET_REG(REG_EPHY_BASE + 0x0020, 0x2340c);
	SET_REG(REG_EPHY_BASE + 0x000c, 0xba0edfff);
	SET_REG(REG_EPHY_BASE + 0x0010, 0x7fcbf9de);
	SET_REG(REG_EPHY_BASE + 0x0b98, 0xd150);
	SET_REG(REG_EPHY_BASE + 0x1224, 0);
	SET_REG(REG_EPHY_BASE + 0x12f0, 0);
	SET_REG(REG_EPHY_BASE + 0x0bc0, 0x7de);
	SET_REG(REG_EPHY_BASE + 0x0bc8, 0x09de);
	SET_REG(REG_EPHY_BASE + 0x070c, 0x0401);
	SET_REG(REG_EPHY_BASE + 0x0a84, 0x1d20);
	SET_REG(REG_EPHY_BASE + 0x06d4, 0x3130);
	SET_REG(REG_EPHY_BASE + 0x0a6c, 0x0818);
	SET_REG(REG_EPHY_BASE + 0x0aec, 0x1000);
	SET_REG(REG_EPHY_BASE + 0x0658, 0x1c00);
	SET_REG(REG_EPHY_BASE + 0x07d4, 0x6900);
	SET_REG(REG_EPHY_BASE + 0x06b8, 0x4);
	SET_REG(REG_EPHY_BASE + 0x06E4, 0x04a0);
	SET_REG(REG_EPHY_BASE + 0x0BBC, 0x1759);
	SET_REG(REG_EPHY_BASE + 0x0bf8, 0x0100);
	SET_REG(REG_EPHY_BASE + 0x0794, 0x9000);
	SET_REG(REG_EPHY_BASE + 0x0788, 0xf);
	SET_REG(REG_EPHY_BASE + 0x1378, 0x1823);
	SET_REG_M(REG_EPHY_BASE + 0x0, 0x23<<24, 0xff<<24);
	SET_REG_M(REG_EPHY_BASE + 0x4, 0x18, 0xff);
	SET_REG(REG_EPHY_BASE + 0x0680, 0x0552);
	SET_REG(REG_EPHY_BASE + 0x1274, 0);
	SET_REG(REG_EPHY_BASE + 0x0b6C, 0x704);
	SET_REG(REG_EPHY_BASE + 0x1340, 0);
	SET_REG(REG_EPHY_BASE + 0x0ba8, 0);
	SET_REG(REG_EPHY_BASE + 0x06c8, 0xc000);
	SET_REG(REG_EPHY_BASE + 0x06e0, 0x8508);
	SET_REG(REG_EPHY_BASE + 0x1360, 0x0137);

	SET_REG(REG_EPHY_BASE + 0x076c, 0x0430);
	SET_REG(REG_EPHY_BASE + 0x06b4, 0xff00);

	SET_REG_M(REG_EPHY_BASE + 0x0778, 0x24 << 8, 0xff << 8);
	SET_REG_M(REG_EPHY_BASE + 0x077c, 0x24, 0xff);

	for (idx = 0; idx < ARRAY_SIZE(train_array); idx++) {
		/*dump_phy_train_val(&train_array[idx]);*/
		parse_train_data(&train_array[idx]);
	}
	SET_REG(REG_PMU_SCU_PLL_WREN,0);
}

static void fh_internal_phy_link_up_cb(void *pri)
{
	SET_REG(REG_EPHY_BASE + 0x0b10, 0x5000);
	SET_REG(REG_EPHY_BASE + 0x0b14, 0x8000);
	SET_REG(REG_EPHY_BASE + 0x0b1c, 0x000e);
	SET_REG(REG_EPHY_BASE + 0x0b20, 0x0004);
	SET_REG(REG_EPHY_BASE + 0x0aec, 0x1800);
	SET_REG(REG_EPHY_BASE + 0x0b10, 0x0000);
	SET_REG(REG_EPHY_BASE + 0x0b14, 0x0000);
}

static void fh_internal_phy_link_down_cb(void *pri)
{

}

int board_eth_init(bd_t *bis)
{
	int rc = 0;

	struct phy_driver board_driver = {0};

	board_driver.phy_info[0].phy_sel = INTERNAL_PHY;
	board_driver.phy_info[0].phy_reset = fh_internal_phy_reset;
	board_driver.phy_info[0].sync_mac_spd = 0;
	board_driver.phy_info[0].phy_inf = PHY_INTERFACE_MODE_MII;
	board_driver.phy_info[0].brd_link_up_cb = fh_internal_phy_link_up_cb,
	board_driver.phy_info[0].brd_link_down_cb = fh_internal_phy_link_down_cb,

	board_driver.phy_info[1].phy_sel = EXTERNAL_PHY;
	board_driver.phy_info[1].phy_reset = fh_gmac_phy_reset;
	board_driver.phy_info[1].sync_mac_spd = gmac_set_spd;
	board_driver.phy_info[1].phy_inf = PHY_INTERFACE_MODE_RMII;

	board_driver.inf_set = phy_inf_set;
	board_driver.phy_sel = phy_sel_func;
	//register phy driver
	genphy_init(&board_driver);

	rc = fh_gmac_initialize(bis);
	return rc;
}

void do_arc_jump(ulong addr)
{
	unsigned int arc_addr;

	arc_addr = ((addr & 0xffff) << 16) | (addr >> 16);

	// ungate arc clk
	SET_REG_M(REG_PMU_CLK_GATE, 0, BIT(22));

	printf("arc addr: 0x%x\n", arc_addr);

	// ARC Reset
	SET_REG(REG_PMU_SCU_PLL_WREN,0x706c6c63);
	SET_REG(REG_PMU_SWRSTN_NSR, 0xFFBFFFFF);


	SET_REG(REG_PMU_A625BOOT0 , 0x7940266B);
	SET_REG(REG_PMU_A625BOOT1 , arc_addr);  // Configure ARC Bootcode start address
	SET_REG(REG_PMU_A625BOOT2 , 0x0F802020);
	SET_REG(REG_PMU_A625BOOT3 , arc_addr);

	// ARC reset released
	SET_REG( REG_PMU_SWRSTN_NSR, 0xFFFFFFFF);

	// wait ramloader done, about 1024 ARC CPU cycle
	udelay(100);

	// start ARC625
	SET_REG_M( REG_PMU_A625_START_CTRL, 0xFF, 0x10);

	SET_REG(REG_PMU_SCU_PLL_WREN,0);

}


#ifdef CONFIG_FH_MMC
static int fh_mci_reset(unsigned int slot_id)
{
	u32 reg;

	reg = 0xffffffff & (~BIT(slot_id ? 1 : 2));
	SET_REG(REG_PMU_SCU_PLL_WREN,0x706c6c63);


	SET_REG(REG_PMU_SWRST_AHB_CTRL, reg);
	while (GET_REG(REG_PMU_SWRST_AHB_CTRL) != 0xffffffff)
		;

	SET_REG(REG_PMU_SCU_PLL_WREN,0);

	return 0;
}

void fh_mci_sys_init(unsigned int slot_id)
{
#define SMP_SHIFT           0

	unsigned int drv_shift;
	unsigned int sam_shift;
	unsigned int reg;


	SET_REG(REG_PMU_SCU_PLL_WREN,0x706c6c63);
	if (slot_id == 1) {
		drv_shift = 12;
		sam_shift = 8;
		fh_mci_reset(slot_id);

		/*//adjust clock phase...*/
		reg = GET_REG(REG_PMU_CLK_GATE1);

		reg &= ~(0xf << drv_shift);
		reg &= ~(0xf << sam_shift);
		reg |= (8 << drv_shift); /*//now drv fixed to 180.*/
		reg |= (SMP_SHIFT << sam_shift);

		SET_REG(REG_PMU_CLK_GATE1, reg);
	} else {
		drv_shift = 20;
		sam_shift = 16;

		fh_mci_reset(slot_id);

		/*//adjust clock phase...*/
		reg = GET_REG(REG_PMU_CLK_GATE1);

		reg &= ~(0xf << drv_shift);
		reg &= ~(0xf << sam_shift);
		reg |= (8 << drv_shift); /*//now drv fixed to 180.*/
		reg |= (SMP_SHIFT << sam_shift);

		SET_REG(REG_PMU_CLK_GATE1, reg);

	}
	SET_REG(REG_PMU_SCU_PLL_WREN,0);
}
#endif

#ifdef CONFIG_FH_GMAC
void GMAC_EarlyInit(void *gmac)
{

}
#endif

void reset_cpu (ulong addr)
{
	SET_REG(REG_PMU_SCU_PLL_WREN,0x706c6c63);

	SET_REG(REG_PMU_SWRST_MAIN_CTRL, 0x7fffffff);
	
	SET_REG(REG_PMU_SCU_PLL_WREN,0);

	while (1)
		;
}

#ifdef CONFIG_GENERIC_MMC
extern int fh_mmc_init(int index);

int board_mmc_init(bd_t *bis)
{
    int ret = 0;
#ifdef FH_MMC_SDC0
        ret = fh_mmc_init(0);
        if (ret)
            printf("No SD0 device found !\n");
#endif

#ifdef FH_MMC_SDC1
    ret = fh_mmc_init(1);
    if (ret)
        printf("No SD1 device found !\n");
#endif

    return ret;
}
#endif


void fh_set_dma_hashdmasel(int en)
{
    if (en) {
        SET_REG_M(REG_PMU_DMA, 1<<16, 1<<16);
    } else {
        SET_REG_M(REG_PMU_DMA, 0<<16, 1<<16);
    }
}
